﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using ST.Common.Util;
using ST.Common.Util.Security;
using ST.WX.Common.Enums;
using ST.WX.Common.Model;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace ST.WX.Common
{
	/// <summary>
	/// 微信基础相关接口（如果使用此类，则不需要再注入IWXAppManager）
	/// </summary>
	public class WXBaseApi
	{
		private readonly ILogger logger;
		private readonly IMemoryCache cache;
		private readonly string CACHE_KEY_PREFIX = "ACCESS_TOKEN_";

		public WXBaseApi(ILogger<WXBaseApi> _logger, IMemoryCache _cache)
		{
			logger = _logger;
			cache = _cache;
		}

		/// <summary>
		/// 校验微信签名，普通的ACCESS_TOKEN
		/// </summary>
		/// <param name="appToken"></param>
		/// <param name="signature">微信加密签名</param>
		/// <param name="timestamp">时间戳</param>
		/// <param name="nonce">随机数</param>
		/// <returns></returns>
		public bool CheckSignature(string appToken, string signature, string timestamp, string nonce)
		{
			try
			{
				string[] paras = new string[] { appToken, timestamp, nonce };
				Array.Sort(paras);
				string computed = EncryptHelper.SHA1Hash(string.Join("", paras));
				return string.Compare(computed, signature, true) == 0;
			}
			catch (WXException ex)
			{
				logger.LogError(ex.InnerException ?? ex, ex.Message);
				throw ex;
			}
			catch (Exception ex)
			{
				logger.LogError(ex.InnerException ?? ex, ex.Message);
				throw new WXException("登录凭证校验出错", ex);
			}
		}

		/// <summary>
		/// 刷新ACCESS_TOKEN
		/// </summary>
		public bool RefreshAccessToken(string appID, string appSecret)
		{
			try
			{
				if (string.IsNullOrEmpty(appID))
				{
					throw new WXException("appID不能为空");
				}
				string cacheKey = CACHE_KEY_PREFIX + appID;
				string respText = HttpHelper.Get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appSecret);
				if (string.IsNullOrEmpty(respText))
				{
					throw new WXException("刷新ACCESS_TOKEN返回为空");
				}
				var ret = Newtonsoft.Json.JsonConvert.DeserializeObject<WXTokenResult>(respText);
				if (string.IsNullOrWhiteSpace(ret.access_token))
				{
					throw new WXException($"刷新ACCESS_TOKEN返回错误码：{ret.errcode}，错误消息：{ret.errmsg}");
				}

				MemoryCacheEntryOptions opts = new MemoryCacheEntryOptions();
				opts.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(ret.expires_in);
				//opts.AbsoluteExpiration = DateTime.SpecifyKind(DateTime.Now.AddSeconds(ret.expires_in), DateTimeKind.Local);//微信官方7200秒过期
				cache.Set(cacheKey, ret.access_token, opts);
				return true;
			}
			catch (WXException ex)
			{
				logger.LogError(ex.InnerException ?? ex, "刷新ACCESS_TOKEN出错");
				throw ex;
			}
			catch (Exception ex)
			{
				logger.LogError(ex.InnerException ?? ex, "刷新ACCESS_TOKEN出错");
				throw new WXException(ex.Message);
			}
		}

		/// <summary>
		/// 获取普通授权ACCESS_TOKEN
		/// </summary>
		public string GetAccessToken(string appID, string appSecret)
		{
			try
			{
				if (string.IsNullOrEmpty(appID))
				{
					throw new WXException("appID不能为空");
				}
				string cacheKey = CACHE_KEY_PREFIX + appID;
				string retCache = cache.Get(cacheKey) as string;
				if (!string.IsNullOrEmpty(retCache))
				{
					return retCache;
				}
				string respText = HttpHelper.Get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appSecret);
				if (string.IsNullOrWhiteSpace(respText))
				{
					throw new Exception("获取ACCESS_TOKEN返回为空");
				}
				var ret = Newtonsoft.Json.JsonConvert.DeserializeObject<WXTokenResult>(respText);
				if (ret.errcode != 0 || string.IsNullOrWhiteSpace(ret.access_token))
				{
					throw new Exception($"获取ACCESS_TOKEN返回错误码：{ret.errcode}，错误消息：{ret.errmsg}");
				}
				MemoryCacheEntryOptions opts = new MemoryCacheEntryOptions();
				opts.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(ret.expires_in);
				//opts.AbsoluteExpiration = DateTime.SpecifyKind(DateTime.Now.AddSeconds(ret.expires_in), DateTimeKind.Local);//微信官方7200秒过期
				cache.Set(CACHE_KEY_PREFIX + appID, ret.access_token, opts);

				return ret.access_token;
			}
			catch (WXException ex)
			{
				logger.LogError(ex.InnerException ?? ex, "获取ACCESS_TOKEN出错");
			}
			catch (Exception ex)
			{
				logger.LogError(ex.InnerException ?? ex, "获取ACCESS_TOKEN出错");
			}
			return string.Empty;
		}
	}
}
